KAFKA-12170: Fix for Connect Cast SMT to correctly transform a Byte array into a string#9950
Conversation
Cast SMT transformation for bytes -> string.
Without this fix, the conversion becomes
ByteBuffer.toString(), which always gives this useless result:
"java.nio.HeapByteBuffer[pos=0 lim=4 cap=4]"
With this change, the byte array is converted into a hex string of the
byte buffer content, for example
"FEDCBA9876543210"
Completed with test case and successfully tried out in a
real database conversion.
It turns out that not all connectors produce a HeapBuffer to store bytes. SAP Hana, specifically, just stores a simple byte array that was not captured by the cast transformer. Completed with test case. Fix for https://issues.apache.org/jira/browse/KAFKA-12170
|
@sknop hello , It seems like there are some style error . |
Thank you @g1geordie for your feedback. I have adjusted the code and it now passes the style check. |
mimaison
left a comment
There was a problem hiding this comment.
Thanks for the PR!
It looks good overall, I just left a few minor comments
kkonstantine
left a comment
There was a problem hiding this comment.
Thanks for the PR @sknop
Left a few comments inline.
| private static String castByteArrayToString(byte[] array) { | ||
| StringBuilder sbuf = new StringBuilder(); | ||
| for (byte b : array) { | ||
| sbuf.append(String.format("%02X", b)); |
There was a problem hiding this comment.
Also, I'm a bit curious. Why are we selecting hex here and not base64 for example?
Hex of course is less efficient.
https://issues.apache.org/jira/browse/KAFKA-12170 does not mention anything about the rational, so I thought I'd ask.
@rhauch wdyt about the representation here?
There was a problem hiding this comment.
The reason I suggested Hex representation was that in the use case I encountered - database PK field encoded as BINARY retrieved via CDC Source Connector - the representation in the SQL CLI was Hex, so choosing the same format made it easy to compare source and target.
I would suggest that base64 encoding would validate its own dedicated transformer, with an ability to choose padding, for example
There was a problem hiding this comment.
As noted in my previous comment, I agree with @kkonstantine that base64 would be preferable. Doing that would align better with the existing Values class used in Connect's header converter mechanism.
If we want to add support for multiple encodings, we would need to have a KIP since it would likely mean changing the SMT configuration.
There was a problem hiding this comment.
If base64 is the better choice we shall go with that. Would we need to adjust the JIRA ticket to reflect this?
I just ran a little test in the JShell:
jshell> byte[] byteArray = new byte[] {(byte) 0xFE, (byte) 0xDC, (byte) 0xBA, (byte) 0x98, 0x76, 0x54, 0x32, 0x10};
byteArray ==> byte[8] { -2, -36, -70, -104, 118, 84, 50, 16 }
jshell> Base64.getEncoder().encode(byteArray)
$2 ==> byte[12] { 47, 116, 121, 54, 109, 72, 90, 85, 77, 104, 65, 61 }
jshell> Base64.getEncoder().encodeToString(byteArray)
$3 ==> "/ty6mHZUMhA="
jshell> Base64.getEncoder().withoutPadding().encodeToString(byteArray)
$4 ==> "/ty6mHZUMhA"
Would you suggest using padding or not?
There was a problem hiding this comment.
Connect's Values code just uses Base64.getEncoder().encodeToString((byte[]) value) and does not disable padding. Seems like we should follow the same pattern here. Thoughts, @kkonstantine ?
There was a problem hiding this comment.
Yes, I agree. encodeToString is the method I had in mind. Used in Values and JsonConverter for a conversion of bytes to string. It's also more efficient.
…rray. If the ByteBuffer does not have a backing array, we use get(byte[]) to extract the content.
|
Before this fix, the changes made in #4820 (KAFKA-6684) resulted in The discussion on PR #4820 also talked about making this compatible with
Actually, the existing code simply outputs the However, given that none of the cast forms of
As mentioned above, the
I agree that it maybe doesn't suffice in all user situations, so if we also want to support other encodings we'd need other config changes that will require using the KIP mechanism to propose such enhancements. WDYT? |
| private static String castByteArrayToString(byte[] array) { | ||
| StringBuilder sbuf = new StringBuilder(); | ||
| for (byte b : array) { | ||
| sbuf.append(String.format("%02X", b)); |
There was a problem hiding this comment.
As noted in my previous comment, I agree with @kkonstantine that base64 would be preferable. Doing that would align better with the existing Values class used in Connect's header converter mechanism.
If we want to add support for multiple encodings, we would need to have a KIP since it would likely mean changing the SMT configuration.
Change made after code review.
| private static String castByteArrayToString(byte[] array) { | ||
| StringBuilder sbuf = new StringBuilder(); | ||
| for (byte b : array) { | ||
| sbuf.append(String.format("%02X", b)); |
There was a problem hiding this comment.
Yes, I agree. encodeToString is the method I had in mind. Used in Values and JsonConverter for a conversion of bytes to string. It's also more efficient.
This matches the internal format used in other parts of the Connect code.
rhauch
left a comment
There was a problem hiding this comment.
One import-related fix, but otherwise looks good.
Change requested by code review.
|
What about documentation? Where is the documentation stored and what is the process to change it? It might be useful to point out that the Cast SMT will create base64 strings for byte arrays. |
|
The documentation is auto-generated from the transforms code, via TransformationDoc. Since this is an existing SMT, there are no changes that that |
|
Thank you @rhauch. Just to make sure I understand this correctly - the types mentioned in the cast string are the target casts, not the source tasks. The current code does not allow casting to a byte array, only casting from a byte array. |
|
If we don't clarify the documentation, then I think users will be very confused. Can you take a stab at improving/expanding the documentation a bit to clarify the input and output types? |
|
Yes of course, totally agree. |
Thank you @rhauch for your feedback.
kkonstantine
left a comment
There was a problem hiding this comment.
This LGTM
Thanks @sknop
|
Test failures are not related: Merging to trunk |
Cast SMT transformation for bytes -> string.
Without this fix, the conversion becomes
ByteBuffer.toString(), which always gives one of these useless results:
"java.nio.HeapByteBuffer[pos=0 lim=4 cap=4]"
"[B@5ec0a365" (for byte array)
With this change, the byte array is converted into a hex string of the
byte buffer content, for example
Completed with test case and successfully tried out in a
real database conversion.
Committer Checklist (excluded from commit message)